/**
 * =====================================================================================
 *       @file  ngx_connection.c
 *      @brief  
 *
 *  Detailed description starts here.
 *
 *   @internal
 *     Created  2012年05月08日 21时40分41秒 
 *    Revision  1.0.0.0
 *    Compiler  gcc/g++
 *
 *     @author  luis
 * =====================================================================================
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include "ngx_connection.h"
#include "ngx_core.h"
#include "ngx_cycle.h"
#include "ngx_string.h"
#include "ngx_errno.h"
#include "ngx_socket.h"

ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle)
{
    int reuseaddr;
    ngx_uint_t          i, tries; 
    ngx_socket_t        s;
    ngx_listening_t    *ls;

    reuseaddr = 1;

    for (tries = 5; tries; tries--)
    {
        ls = cycle->listening.elts;    
        for (i = 0; i < cycle->listening.nelts; i++)
        {
            if (ls[i].fd != -1)
            {
                continue;
            }

            s = ngx_socket(ls[i].sockaddr->sa_family, ls[i].type, 0);            
            if (s == -1)
            {
                perror("ngx_socket failed");
                return NGX_ERROR;
            } 

            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *)&reuseaddr, sizeof(int)) == -1)
            {
                ngx_close_socket(s);
                return NGX_ERROR;                    
            }

            if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1)
            {
                if (ngx_socket_errno == NGX_EADDRINUSE)
                {
                    continue;
                }               
                
                ngx_close_socket(s);        
                return NGX_ERROR;
            }

            if (listen(s, ls[i].backlog) == -1)
            {
                perror("listen failed");
                ngx_close_socket(s);        
                return NGX_ERROR;
            }
            
            ls[i].listen = 1;
            ls[i].fd = s;
        }

        usleep(500);
    }

    return NGX_OK;
}

ngx_connection_t *ngx_get_connection(int s)
{
    ngx_uint_t          instance;    
    ngx_event_t        *rev, *wev;
    ngx_connection_t   *c;

    c = ngx_cycle->free_connections;
    if (c == NULL)
    {
        //XXX 回收一些连接供新连接使用
        //ngx_drain_connections(); 
        c = ngx_cycle->free_connections;
    }

    if (c == NULL)
    {
        perror("worker connections are not enough");
        return NULL;
    }

    ngx_cycle->free_connections = c->data;
    ngx_cycle->free_connection_n--;

    rev = c->read;
    wev = c->write;

    ngx_memzero(c, sizeof(ngx_connection_t));

    c->read = rev;
    c->write = wev;
    c->fd = s;

    instance = rev->instance;

    ngx_memzero(rev, sizeof(ngx_event_t));
    ngx_memzero(wev, sizeof(ngx_event_t));

    rev->instance = !instance;
    wev->instance = !instance;

    rev->index = NGX_INVALID_INDEX;
    wev->index = NGX_INVALID_INDEX;

    rev->data = c;
    wev->data = c;
    wev->write = 1;

    return c;
}

void ngx_free_connection(ngx_connection_t *c)
{
    c->data = ngx_cycle->free_connections;
    ngx_cycle->free_connections = c;
    ngx_cycle->free_connection_n++;
}
